package fei.typhoon.engine.math;

import android.graphics.PointF;

public class Collision {

	private static Collision instance = null;

	public static Collision get() {
		if (instance == null) {
			instance = new Collision();
		}
		return instance;
	}

	/*
	 * 计算多边形polygon在坐标轴axis上得投影，得到最小值min和最大值max
	 */
	private void projectPolygon(PointF axis, FeiPolygon polygon, Extreme extreme) {
		float[] vertexs = polygon.getVertexs();
		float d = vectDot(axis, new PointF(vertexs[0], vertexs[1]));
		extreme.min = d;
		extreme.max = d;

		int vertexCount = vertexs.length;
		for (int i = 0, j = 0; i < vertexCount; i++, j += 2) {
			d = vectDot(axis, new PointF(vertexs[j], vertexs[j + 1]));
			if (d < extreme.min) {
				extreme.min = d;
			} else {
				if (d > extreme.max) {
					extreme.max = d;
				}
			}
		}
	}

	/*
	 * 计算两个投影得距离
	 */
	private float intervalDistance(float minA, float maxA, float minB,
			float maxB) {
		return (minA < minB) ? (minB - maxA) : (minA - maxB);
	}

	// 向量构造器
	private PointF vectMake(float x, float y) {
		PointF v = new PointF(x, y);
		return v;
	}

	// 向量点乘
	private float vectDot(PointF v1, PointF v2) {
		return v1.x * v2.x + v1.y * v2.y;
	}

	// 向量减法
	private PointF vectSub(PointF v1, PointF v2) {
		return vectMake(v1.x - v2.x, v1.y - v2.y);
	}

	// 向量的垂直向量
	private PointF vectPerp(PointF v) {
		return vectMake(-v.y, v.x);
	}

	public boolean isPolygonsCollision(FeiPolygon a, FeiPolygon b) {
		if (a.getVertexs().length < 2 || a.getVertexs().length % 2 != 0
				|| b.getVertexs().length < 2 || b.getVertexs().length % 2 != 0) {
			return false;
		}
		PointF edge = null, axis = null;
		float minA = 0, maxA = 0, minB = 0, maxB = 0;
		float[] vertexsA = a.getVertexs(), vertexsB = b.getVertexs();
		int vertexCountA = (vertexsA.length / 2), vertexCountB = (vertexsB.length / 2);
		PointF[] vpa = new PointF[vertexCountA], vpb = new PointF[vertexCountB];
		for (int i = 0, j = 0; i < vertexCountA; i++, j += 2) {
			vpa[i] = new PointF(vertexsA[j], vertexsA[j + 1]);
		}
		for (int i = 0, j = 0; i < vertexCountB; i++, j += 2) {
			vpb[i] = new PointF(vertexsB[j], vertexsB[j + 1]);
		}
		for (int i = 0, j = vertexCountA - 1; i < vertexCountA + vertexCountB; j = i, i++) {
			// 通过顶点取得每个边
			if (i < vertexCountA) {
				edge = vectSub(vpa[i], vpa[j]);
			} else {
				edge = vectSub(vpb[i - vertexCountA], vpb[j - vertexCountA]);
			}

			axis = vectPerp(edge); // 向量的垂直向量

			// 以边的垂线为坐标轴进行投影，取得投影线段[min, max]
			Extreme eA = new Extreme(minA, maxA);
			Extreme eB = new Extreme(minB, maxB);
			projectPolygon(axis, a, eA);
			projectPolygon(axis, b, eB);
			minA = eA.min;
			maxA = eA.max;
			minB = eB.min;
			maxB = eB.max;

			// 检查两个投影的距离，如果两投影没有重合部分，那么可以判定这两个多边形没有碰撞
			if (intervalDistance(minA, maxA, minB, maxB) > 0) {
				return false;
			}
		}

		return true;
	}

	public static boolean polygonsCollision(FeiPolygon a, FeiPolygon b) {
		return (get().isPolygonsCollision(a, b));
	}

	private class Extreme {
		float min = 0;
		float max = 0;

		public Extreme(float min, float max) {
			this.min = min;
			this.max = max;
		}
	}

}
